Skip to content

Conversation

cs01
Copy link
Contributor

@cs01 cs01 commented Oct 20, 2025

Summary

Fix FindProcesses to respect Android's hidepid=2 security model and enable name matching for Android apps.

Problem

  1. Called adb shell pidof or adb shell ps directly, bypassing Android's process visibility restrictions
  2. Name matching failed for Android apps - searched for com.example.myapp but GDB Remote Protocol reports app_process64

Android apps fork from Zygote, so /proc/PID/exe points to app_process64 for all apps. The actual package name is only in /proc/PID/cmdline. The previous implementation applied name filters without supplementing with cmdline, so searches failed.

Fix

  • Delegate to lldb-server via GDB Remote Protocol (respects hidepid=2)
  • Get all visible processes, supplement zygote/app_process entries with cmdline, then apply name matching
  • Only fetch cmdline for zygote apps (performance), parallelize with xargs -P 8
  • Remove redundant code (GDB Remote Protocol already provides GID/arch)

Test Results

Before this fix:

(lldb) platform process list
error: no processes were found on the "remote-android" platform

(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
5276   359    u0_a192                                   com.example.hellojni
                         ^^^^^^^^ Missing triple!

After this fix:

(lldb) platform process list
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
1      0      root       aarch64-unknown-linux-android  init
2      0      root                                      [kthreadd]
359    1      system     aarch64-unknown-linux-android  app_process64
5276   359    u0_a192    aarch64-unknown-linux-android  com.example.hellojni
5357   5355   u0_a192    aarch64-unknown-linux-android  sh
5377   5370   u0_a192    aarch64-unknown-linux-android  lldb-server
                          ^^^^^^^^ User-space processes now have triples!

(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
5276   359    u0_a192    aarch64-unknown-linux-android  com.example.hellojni


(lldb) process attach -n com.example.hellojni
Process 5276 stopped
* thread #1, name = 'example.hellojni', stop reason = signal SIGSTOP

Test Plan

With an Android device/emulator connected:

  1. Start lldb-server on device:
adb push lldb-server /data/local/tmp/
adb shell chmod +x /data/local/tmp/lldb-server
adb shell /data/local/tmp/lldb-server platform  --listen 127.0.0.1:9500 --server
  1. Connect from LLDB:
(lldb) platform select remote-android
(lldb) platform connect connect://127.0.0.1:9500
(lldb) platform process list
  1. Verify:
    • platform process list returns all processes with triple information
    • platform process list -n com.example.app finds Android apps by package name
    • process attach -n com.example.app successfully attaches to Android apps

Impact

Restores platform process list on Android with architecture information and package name lookup. All name matching modes now work correctly.

Fixes #164192

@cs01 cs01 requested a review from JDevlieghere as a code owner October 20, 2025 23:00
@llvmbot llvmbot added the lldb label Oct 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 20, 2025

@llvm/pr-subscribers-lldb

Author: Chad Smith (cs01)

Changes

Summary

After #160931, platform process list without arguments returns no processes on remote Android, breaking the ability to enumerate processes. This occurred because FindProcesses fell back to GDB Remote Protocol only when MatchAllProcesses() was true, missing other important scenarios.

Bug Description

After the implementation of FindProcesses in PlatformAndroid (#160931), platform process list without arguments fails to return any processes:

(lldb) platform process list
error: no processes were found on the "remote-android" platform

Additionally, results were inconsistent between different query modes:

  • platform process list -n com.example.hellojni returned processes without triple info
  • platform process list -p 5276 returned correct triple info
  • platform process list (all processes) returned nothing

Root Cause

The original implementation used a simple MatchAllProcesses() check to decide when to fall back to GDB Remote Protocol. This logic was too narrow and missed important cases like:

  • Wildcard/regex name matching (-s, -e, -c, -r flags)
  • Filtering by architecture, user, PID, etc.
  • Enumerating all processes

Why Android is Different from Regular Linux

On regular Linux, process enumeration reads /proc/[pid]/exe, which is a symlink to the actual binary (e.g., /usr/bin/firefox, /bin/bash). This gives you the canonical process name directly. The /proc/[pid]/cmdline contains arguments which can be misleading (e.g., python script.py - do you want "python" or "script.py"?), so Linux platforms use the exe symlink as the reliable source.

Android breaks this assumption due to the Zygote process model:

  • Android apps are forked from a pre-warmed Java VM called Zygote
  • /proc/[pid]/exe points to app_process64 for all Android apps (not the actual app)
  • The actual package name (e.g., com.example.hellojni) only appears in /proc/[pid]/cmdline
  • GDB Remote Protocol reads exe and reports the binary name (app_process64), not the package name
  • To find processes by package name AND support wildcard/regex matching, we need to search cmdline

Solution

Use ps -A -o PID,ARGS to get process list with cmdline information for all remote Android queries.

Remote Android:

  • Use ps -A to list all processes with their command lines
  • Match process name against cmdline (contains Android package names)
  • Support all name matching modes (exact, starts-with, ends-with, contains, regex)
  • Read ELF header from /proc/[pid]/exe for architecture (e_machine at offset 18)
  • Read /proc/[pid]/status for parent PID, UIDs, GIDs
  • Read /proc/[pid]/cmdline for command line arguments (or package name)

These all work now

  • platform process list -n com.example.app
  • platform process list -s com.example
  • platform process list -c example
  • platform process list -r "com\..*\.app"
  • process attach -n com.example.app

Test Results

Before this fix:

(lldb) platform process list
error: no processes were found on the "remote-android" platform

(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
5276   359    u0_a192                                   com.example.hellojni
                         ^^^^^^^^ Missing triple!

After this fix:

(lldb) platform process list
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
1      0      root       aarch64-unknown-linux-android  init
2      0      root                                      [kthreadd]
359    1      system     aarch64-unknown-linux-android  app_process64
5276   359    u0_a192    aarch64-unknown-linux-android  com.example.hellojni
5357   5355   u0_a192    aarch64-unknown-linux-android  sh
5377   5370   u0_a192    aarch64-unknown-linux-android  lldb-server
                          ^^^^^^^^ User-space processes now have triples!

(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID    PARENT USER       TRIPLE                         NAME
====== ====== ========== ============================== ============================
5276   359    u0_a192    aarch64-unknown-linux-android  com.example.hellojni


(lldb) process attach -n com.example.hellojni
Process 5276 stopped
* thread #<!-- -->1, name = 'example.hellojni', stop reason = signal SIGSTOP

Test Plan

With an Android device/emulator connected:

  1. Start lldb-server on device:
adb push lldb-server /data/local/tmp/
adb shell chmod +x /data/local/tmp/lldb-server
adb shell /data/local/tmp/lldb-server platform  --listen 127.0.0.1:9500 --server
  1. Connect from LLDB:
(lldb) platform select remote-android
(lldb) platform connect connect://127.0.0.1:9500
(lldb) platform process list
  1. Verify:
    • platform process list returns all processes with triple information
    • platform process list -n com.example.app finds Android apps by package name
    • process attach -n com.example.app successfully attaches to Android apps

Impact

Restores platform process list on Android with architecture information and package name lookup. All name matching modes now work correctly.

Fixes #164192


Full diff: https://github.com/llvm/llvm-project/pull/164333.diff

2 Files Affected:

  • (modified) lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp (+219-68)
  • (modified) lldb/source/Plugins/Platform/Android/PlatformAndroid.h (+3-1)
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
index 57d88f615e2b3..4b93cfa9b3797 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -10,10 +10,13 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/UriParser.h"
 #include "lldb/ValueObject/ValueObject.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/Base64.h"
 
 #include "AdbClient.h"
 #include "PlatformAndroid.h"
@@ -571,40 +574,98 @@ void PlatformAndroid::PopulateProcessCommandLine(
   process_info.SetArguments(process_args, false);
 }
 
-// Helper function to populate architecture from /proc/[pid]/exe
+// Helper function to populate architecture from /proc/[pid]/exe by reading ELF
+// header
 void PlatformAndroid::PopulateProcessArchitecture(
     lldb::pid_t pid, ProcessInstanceInfo &process_info) {
-  // Read /proc/[pid]/exe to get executable path for architecture detection
+  // Read the first 20 bytes of /proc/[pid]/exe to parse the ELF header
+  // We need the ELF identification (16 bytes) plus e_machine field (2 bytes at
+  // offset 18)
   Status error;
-  AdbClientUP exe_adb = GetAdbClient(error);
+  AdbClientUP elf_adb = GetAdbClient(error);
   if (error.Fail())
     return;
 
-  std::string exe_output;
-  StreamString exe_cmd;
-  exe_cmd.Printf("readlink /proc/%llu/exe 2>/dev/null",
+  std::string elf_header_base64;
+  StreamString elf_cmd;
+  // Use dd to read just the ELF header (first 20 bytes is enough for e_machine)
+  // Output as base64 to avoid parsing issues with binary data
+  elf_cmd.Printf("dd if=/proc/%llu/exe bs=20 count=1 2>/dev/null | base64",
                  static_cast<unsigned long long>(pid));
-  Status exe_error = exe_adb->Shell(exe_cmd.GetData(), seconds(5), &exe_output);
+  Status elf_error =
+      elf_adb->Shell(elf_cmd.GetData(), seconds(5), &elf_header_base64);
 
-  if (exe_error.Fail() || exe_output.empty())
+  if (elf_error.Fail() || elf_header_base64.empty())
     return;
 
-  exe_output = llvm::StringRef(exe_output).trim().str();
+  // Decode base64 using LLVM's base64 decoder
+  std::vector<char> header_bytes;
+  llvm::Error decode_error = llvm::decodeBase64(
+      llvm::StringRef(elf_header_base64).trim(), header_bytes);
+
+  Log *log = GetLog(LLDBLog::Platform);
+  if (decode_error) {
+    LLDB_LOGF(log, "PlatformAndroid::%s base64 decode failed for PID %llu: %s",
+              __FUNCTION__, static_cast<unsigned long long>(pid),
+              llvm::toString(std::move(decode_error)).c_str());
+    return;
+  }
+
+  LLDB_LOGF(log, "PlatformAndroid::%s decoded %zu bytes for PID %llu",
+            __FUNCTION__, header_bytes.size(),
+            static_cast<unsigned long long>(pid));
+
+  // Need at least 20 bytes for ELF header check
+  if (header_bytes.size() < 20) {
+    LLDB_LOGF(log, "PlatformAndroid::%s insufficient bytes (%zu < 20)",
+              __FUNCTION__, header_bytes.size());
+    return;
+  }
+
+  // Use DataExtractor to parse ELF header
+  DataExtractor extractor(header_bytes.data(), header_bytes.size(),
+                          eByteOrderLittle, 4);
+  lldb::offset_t offset = 0;
+
+  // Verify ELF magic number (0x7f 'E' 'L' 'F')
+  if (extractor.GetU8(&offset) != 0x7f || extractor.GetU8(&offset) != 'E' ||
+      extractor.GetU8(&offset) != 'L' || extractor.GetU8(&offset) != 'F') {
+    LLDB_LOGF(log,
+              "PlatformAndroid::%s invalid ELF magic at offset 0 for PID %llu",
+              __FUNCTION__, static_cast<unsigned long long>(pid));
+    return;
+  }
+
+  // Get ELF class (32-bit vs 64-bit) from e_ident[EI_CLASS]
+  bool is_64bit = (extractor.GetU8(&offset) == llvm::ELF::ELFCLASS64);
+
+  // e_machine is at offset 18 in both 32-bit and 64-bit ELF headers
+  offset = offsetof(llvm::ELF::Elf32_Ehdr, e_machine);
+  uint16_t e_machine = extractor.GetU16(&offset);
+
+  LLDB_LOGF(log, "PlatformAndroid::%s ELF class=%s e_machine=0x%x for PID %llu",
+            __FUNCTION__, is_64bit ? "64-bit" : "32-bit", e_machine,
+            static_cast<unsigned long long>(pid));
 
-  // Determine architecture from exe path
   ArchSpec arch;
-  if (exe_output.find("64") != std::string::npos ||
-      exe_output.find("arm64") != std::string::npos ||
-      exe_output.find("aarch64") != std::string::npos) {
+  switch (e_machine) {
+  case llvm::ELF::EM_ARM:
+    arch.SetTriple("armv7-unknown-linux-android");
+    break;
+  case llvm::ELF::EM_AARCH64:
     arch.SetTriple("aarch64-unknown-linux-android");
-  } else if (exe_output.find("x86_64") != std::string::npos) {
-    arch.SetTriple("x86_64-unknown-linux-android");
-  } else if (exe_output.find("x86") != std::string::npos ||
-             exe_output.find("i686") != std::string::npos) {
+    break;
+  case llvm::ELF::EM_386:
     arch.SetTriple("i686-unknown-linux-android");
-  } else {
-    // Default to armv7 for 32-bit ARM (most common on Android)
-    arch.SetTriple("armv7-unknown-linux-android");
+    break;
+  case llvm::ELF::EM_X86_64:
+    arch.SetTriple("x86_64-unknown-linux-android");
+    break;
+  default:
+    LLDB_LOGF(log, "PlatformAndroid::%s unknown e_machine=0x%x", __FUNCTION__,
+              e_machine);
+    arch.SetTriple("unknown-unknown-linux-android");
+    break;
   }
 
   if (arch.IsValid())
@@ -623,26 +684,22 @@ PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
   if (IsHost())
     return PlatformLinux::FindProcesses(match_info, proc_infos);
 
-  // Remote Android platform: implement process name lookup using 'pidof' over
-  // adb.
+  // Remote Android: Always use 'ps' to get process list with cmdline
+  // information (which contains Android package names). This ensures consistent
+  // naming for all queries (by name, PID, arch, user, etc.) - Android apps
+  // will show their package names instead of "app_process64".
 
-  // LLDB stores the search name in GetExecutableFile() (even though it's
-  // actually a process name like "com.android.chrome" rather than an
-  // executable path). If no search name is provided, we can't use
-  // 'pidof', so return early with no results.
   const ProcessInstanceInfo &match_process_info = match_info.GetProcessInfo();
-  if (!match_process_info.GetExecutableFile() ||
-      match_info.GetNameMatchType() == NameMatch::Ignore) {
-    return 0;
-  }
+  std::string process_name;
+  NameMatch name_match_type = NameMatch::Ignore;
 
-  // Extract the process name to search for (typically an Android package name
-  // like "com.example.app" or binary name like "app_process64")
-  std::string process_name = match_process_info.GetExecutableFile().GetPath();
-  if (process_name.empty())
-    return 0;
+  // If there's a name to match, extract it
+  if (match_process_info.GetExecutableFile()) {
+    process_name = match_process_info.GetExecutableFile().GetPath();
+    name_match_type = match_info.GetNameMatchType();
+  }
 
-  // Use adb to find the process by name
+  // Use adb to get process list
   Status error;
   AdbClientUP adb(GetAdbClient(error));
   if (error.Fail()) {
@@ -652,44 +709,39 @@ PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
     return 0;
   }
 
-  // Use 'pidof' command to get PIDs for the process name.
-  // Quote the process name to handle special characters (spaces, etc.)
-  std::string pidof_output;
-  StreamString command;
-  command.Printf("pidof '%s'", process_name.c_str());
-  error = adb->Shell(command.GetData(), seconds(5), &pidof_output);
+  std::string ps_output;
+  error = adb->Shell("ps -A -o PID,ARGS", seconds(5), &ps_output);
 
   if (error.Fail()) {
     Log *log = GetLog(LLDBLog::Platform);
-    LLDB_LOG(log, "PlatformAndroid::{} 'pidof {}' failed: {}", __FUNCTION__,
-             process_name.c_str(), error.AsCString());
-    return 0;
-  }
-
-  // Parse PIDs from pidof output.
-  // Note: pidof can return multiple PIDs (space-separated) if multiple
-  // instances of the same executable are running.
-  pidof_output = llvm::StringRef(pidof_output).trim().str();
-  if (pidof_output.empty()) {
-    Log *log = GetLog(LLDBLog::Platform);
-    LLDB_LOGF(log, "PlatformAndroid::%s no process found with name '%s'",
-              __FUNCTION__, process_name.c_str());
+    LLDB_LOG(log, "PlatformAndroid::{} 'ps -A' failed: {}", __FUNCTION__,
+             error.AsCString());
     return 0;
   }
 
-  // Split the output by whitespace to handle multiple PIDs
-  llvm::SmallVector<llvm::StringRef, 8> pid_strings;
-  llvm::StringRef(pidof_output).split(pid_strings, ' ', -1, false);
-
   Log *log = GetLog(LLDBLog::Platform);
 
-  // Process each PID and gather information
+  llvm::SmallVector<llvm::StringRef, 256> lines;
+  llvm::StringRef(ps_output).split(lines, '\n', -1, false);
+
   uint32_t num_matches = 0;
-  for (llvm::StringRef pid_str : pid_strings) {
-    pid_str = pid_str.trim();
-    if (pid_str.empty())
+
+  for (llvm::StringRef line : lines) {
+    line = line.trim();
+    if (line.empty())
       continue;
 
+    if (line.starts_with("PID"))
+      continue;
+
+    // Parse PID (first whitespace-separated field)
+    auto space_pos = line.find(' ');
+    if (space_pos == llvm::StringRef::npos)
+      continue;
+
+    llvm::StringRef pid_str = line.substr(0, space_pos);
+    llvm::StringRef cmdline = line.substr(space_pos + 1).trim();
+
     lldb::pid_t pid;
     if (!llvm::to_integer(pid_str, pid)) {
       LLDB_LOGF(log, "PlatformAndroid::%s failed to parse PID from: '%s'",
@@ -697,23 +749,57 @@ PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
       continue;
     }
 
+    bool name_matches = true;
+    if (name_match_type != NameMatch::Ignore && !process_name.empty()) {
+      name_matches = false;
+      switch (name_match_type) {
+      case NameMatch::Equals:
+        name_matches = (cmdline == process_name);
+        break;
+      case NameMatch::StartsWith:
+        name_matches = cmdline.starts_with(process_name);
+        break;
+      case NameMatch::EndsWith:
+        name_matches = cmdline.ends_with(process_name);
+        break;
+      case NameMatch::Contains:
+        name_matches = cmdline.contains(process_name);
+        break;
+      case NameMatch::RegularExpression: {
+        llvm::Regex regex(process_name);
+        name_matches = regex.match(cmdline);
+        break;
+      }
+      default:
+        name_matches = true;
+        break;
+      }
+    }
+
+    if (!name_matches)
+      continue;
+
     ProcessInstanceInfo process_info;
     process_info.SetProcessID(pid);
-    process_info.GetExecutableFile().SetFile(process_name,
-                                             FileSpec::Style::posix);
 
-    // Populate additional process information
+    // Set the executable name from cmdline (first token, typically)
+    llvm::StringRef exe_name = cmdline;
+    auto first_space = cmdline.find(' ');
+    if (first_space != llvm::StringRef::npos)
+      exe_name = cmdline.substr(0, first_space);
+
+    process_info.GetExecutableFile().SetFile(exe_name, FileSpec::Style::posix);
+
     PopulateProcessStatusInfo(pid, process_info);
     PopulateProcessCommandLine(pid, process_info);
     PopulateProcessArchitecture(pid, process_info);
 
-    // Check if this process matches the criteria
     if (match_info.Matches(process_info)) {
       proc_infos.push_back(process_info);
       num_matches++;
 
       LLDB_LOGF(log, "PlatformAndroid::%s found process '%s' with PID %llu",
-                __FUNCTION__, process_name.c_str(),
+                __FUNCTION__, exe_name.str().c_str(),
                 static_cast<unsigned long long>(pid));
     }
   }
@@ -721,6 +807,71 @@ PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
   return num_matches;
 }
 
+bool PlatformAndroid::GetProcessInfo(lldb::pid_t pid,
+                                     ProcessInstanceInfo &proc_info) {
+  // On native Android or when remote, use ps to get process info with
+  // cmdline (which contains Android package names).
+  if (IsHost() || pid == LLDB_INVALID_PROCESS_ID)
+    return PlatformLinux::GetProcessInfo(pid, proc_info);
+
+  // Use ps to get process info for this specific PID
+  Status error;
+  AdbClientUP adb(GetAdbClient(error));
+  if (error.Fail())
+    return false;
+
+  StreamString cmd;
+  cmd.Printf("ps -A -o PID,ARGS -p %llu", static_cast<unsigned long long>(pid));
+
+  std::string ps_output;
+  error = adb->Shell(cmd.GetData(), seconds(5), &ps_output);
+  if (error.Fail())
+    return false;
+
+  // Parse ps output - should be 2 lines: header + process
+  llvm::SmallVector<llvm::StringRef, 2> lines;
+  llvm::StringRef(ps_output).split(lines, '\n', -1, false);
+
+  for (llvm::StringRef line : lines) {
+    line = line.trim();
+    if (line.empty() || line.starts_with("PID"))
+      continue;
+
+    // Parse PID and cmdline
+    auto space_pos = line.find(' ');
+    if (space_pos == llvm::StringRef::npos)
+      continue;
+
+    llvm::StringRef pid_str = line.substr(0, space_pos);
+    llvm::StringRef cmdline = line.substr(space_pos + 1).trim();
+
+    lldb::pid_t parsed_pid;
+    if (!llvm::to_integer(pid_str, parsed_pid) || parsed_pid != pid)
+      continue;
+
+    // Found our process, populate info
+    proc_info.Clear();
+    proc_info.SetProcessID(pid);
+
+    // Set executable name from cmdline (first token)
+    llvm::StringRef exe_name = cmdline;
+    auto first_space = cmdline.find(' ');
+    if (first_space != llvm::StringRef::npos)
+      exe_name = cmdline.substr(0, first_space);
+
+    proc_info.GetExecutableFile().SetFile(exe_name, FileSpec::Style::posix);
+
+    // Populate additional info
+    PopulateProcessStatusInfo(pid, proc_info);
+    PopulateProcessCommandLine(pid, proc_info);
+    PopulateProcessArchitecture(pid, proc_info);
+
+    return true;
+  }
+
+  return false;
+}
+
 std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {
   auto sync_service = std::make_unique<AdbSyncService>(m_device_id);
   error = sync_service->SetupSyncConnection();
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
index e771c6ae97d4d..fe28913b417b7 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
@@ -60,7 +60,9 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
   uint32_t GetDefaultMemoryCacheLineSize() override;
 
   uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
-                         ProcessInstanceInfoList &proc_infos) override;
+                         ProcessInstanceInfoList &process_infos) override;
+
+  bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override;
 
 protected:
   const char *GetCacheHostname() override;

exe_name = cmdline.substr(0, first_space);

process_info.GetExecutableFile().SetFile(exe_name, FileSpec::Style::posix);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs multiple adb commands for each pid on the Android device, and an unfiltered query returns around 1000 pids. Empirically, this function (called via Python API or platform process list) takes over 1 minute to finish even on a local device with USB-3.2 connection.

Maybe we should batch the call for all processes into one, and then parse the response?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed it was slow too. However it did not seem slower than before. But I like the idea of batching the calls. I can do that tomorrow.

command.Printf("pidof '%s'", process_name.c_str());
error = adb->Shell(command.GetData(), seconds(5), &pidof_output);
std::string ps_output;
error = adb->Shell("ps -A -o PID,ARGS", seconds(5), &ps_output);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the point of querying all processes, when the Android security model only allows attaching to the processes running as the same user as lldb-server. Shouldn't we list only those processes that we can debug?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the full list of potentially non-debuggable processes is shown on non-Android platforms too. I can check tomorrow. I would prefer to follow whatever convention lldb has for this in general.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Darwin, lldb has an extra service that can prompt for root credentials and then allow debugging root processes. Presumably lldb-server could be taught to do the same thing on other systems. It's also just useful when you are debugging a remote system to be able to see what's going on there, even if you can't attach to everything you see.

Copy link
Contributor Author

@cs01 cs01 Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also just useful when you are debugging a remote system to be able to see what's going on there, even if you can't attach to everything you see.

Agreed. It would be surprising to me if I ran platform process list and it applied some kind of extra filtering.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It would be surprising to me if I ran platform process list and it applied some kind of extra filtering.

That is the Android (Linux) security model.

On Android, when running inside a process/package, you are not supposed to see other processes, by design.

  • When lldb-server runs inside a package/user, it should only see the processes in the package.
  • When lldb-server runs as shell or root on the Android device, it will already have access to all process on the system.

By using this adb-based approach, you are bypassing the security model. On a non-Android Linux machine that was configured with hidepid=2, I don't think you will see processes from other users. That means this PR is actually introducing behavior that is inconsistent across Android and Linux (and goes against the security model).

Copy link
Contributor Author

@cs01 cs01 Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the additional info, that makes sense. I updated to comply with the security model by delegating to m_remote_platform_sp->FindProcesses (which was being done before this PR).

Now all processes that lldb-server can see are fetched, then supplemented with /proc/PID/cmdline to get the actual package names (e.g., "com.example.myapp" instead of "zygote"), and then name matching is applied.

It is ready for another review.

@cs01 cs01 force-pushed the cs01/fix-android-process-list branch 4 times, most recently from 2c94484 to 768b2ce Compare October 21, 2025 22:18
Copy link

github-actions bot commented Oct 21, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@cs01 cs01 force-pushed the cs01/fix-android-process-list branch from 768b2ce to 8384227 Compare October 21, 2025 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[LLDB/Android] Cannot list processes

4 participants